/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package be.contribute;

import be.contribute.common.pojo.Temperature;
import be.contribute.util.DateAxis310;
import com.sun.jersey.api.client.UniformInterfaceException;
import eu.hansolo.enzo.common.Section;
import eu.hansolo.enzo.common.SymbolType;
import eu.hansolo.enzo.gauge.SimpleGauge;
import eu.hansolo.enzo.gauge.SimpleGaugeBuilder;
import eu.hansolo.enzo.lcd.Alarm;
import eu.hansolo.enzo.lcd.LcdClock;
import eu.hansolo.enzo.lcd.LcdClockBuilder;
import eu.hansolo.enzo.onoffswitch.IconSwitch;
import eu.hansolo.enzo.onoffswitch.SelectionEvent;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.text.Text;
import javafx.stage.Popup;
import javafx.stage.Stage;
import javafx.util.StringConverter;

/**
 *
 * @author wimvandecasteele
 */
public class PiHomeApp extends Application {
    private static final String[] STYLE_CLASSES = {
        "lcd-beige",
        "lcd-blue",
        "lcd-orange",
        "lcd-red",
        "lcd-yellow",
        "lcd-white",
        "lcd-gray",
        "lcd-black",
        "lcd-green",
        "lcd-green-darkgreen",
        "lcd-blue2",
        "lcd-blue-black",
        "lcd-blue-darkblue",
        "lcd-blue-lightblue",
        "lcd-blue-gray",
        "lcd-standard",
        "lcd-lightgreen",
        "lcd-standard-green",
        "lcd-blue-blue",
        "lcd-red-darkred",
        "lcd-darkblue",
        "lcd-purple",
        "lcd-black-red",
        "lcd-darkgreen",
        "lcd-amber",
        "lcd-lightblue",
        "lcd-green-black",
        "lcd-yellow-black",
        "lcd-black-yellow",
        "lcd-lightgreen-black",
        "lcd-darkpurple",
        "lcd-darkamber",
        "lcd-blue-lightblue2",
        "lcd-gray-purple",
        "lcd-sections",
        "lcd-flat-turqoise",
        "lcd-flat-gree-sea",
        "lcd-flat-emerland",
        "lcd-flat-nephritis",
        "lcd-flat-peter-river",
        "lcd-flat-belize-hole",
        "lcd-flat-amethyst",
        "lcd-flat-wisteria",
        "lcd-flat-sunflower",
        "lcd-flat-orange",
        "lcd-flat-carrot",
        "lcd-flat-pumpkin",
        "lcd-flat-alizarin",
        "lcd-flat-pomegranate",
        "lcd-flat-clouds",
        "lcd-flat-silver",
        "lcd-flat-concrete",
        "lcd-flat-asbestos",
        "lcd-flat-wet-asphalt",
        "lcd-flat-midnight-blue"
    };
    private LcdClock       vClock;
    private int            styleClassCounter;
    private final AlarmCommand     vAlarmEvent = new AlarmCommand();      
    private final HomeClient client = new HomeClient("http://192.168.0.234:8080/PiHome");
    private static int     noOfNodes  = 0;
    private SimpleGauge    vThermoMetreKitchten, vHumMetreKitchen, vThermoMetreBath, vHumMetreBath;
    private long           lastTimerCall;
    private AnimationTimer timer;
    private IconSwitch vAircoButton;
    private IconSwitch vHeaterButton;
    private static final DateTimeFormatter   DTF = DateTimeFormatter.ofPattern("dd.MM.yy HH:mm");
    private LineChart<LocalDateTime, Number> lineChart,lineChart2;
    private Popup                            popup;
    private ImageView vCamView;

    @Override public void init() {
        vAircoButton = createAircoSwitch(1);
        vHeaterButton= createHeatControlSwitch(1);
        
        vThermoMetreKitchten = createTempGauge(200);
        vHumMetreKitchen = createHumGauge(200);
        vThermoMetreBath = createTempGauge(250);
        vHumMetreBath = createHumGauge(250);

        lastTimerCall = System.nanoTime() + 2_000_000_000l;
        timer = new AnimationTimer() {
            @Override public void handle(long now) {
                if (now > lastTimerCall + 10_000_000_000l) {
                    try
                    {
                        Temperature vTemp = client.getRoomTemperature(1);
                        Temperature vTempBath = client.getRoomTemperature(2);
                        System.out.print("F5; ");
                        vThermoMetreKitchten.setValue(vTemp.getTempValue());
                        vHumMetreKitchen.setValue(vTemp.getHumValue());
                        vThermoMetreBath.setValue(vTempBath.getTempValue());
                        vHumMetreBath.setValue(vTempBath.getHumValue());
                    }
                    catch(UniformInterfaceException ex)
                    {
                         System.out.println("Ex: " + ex.getMessage());
                         System.out.println("Ocurred: " + new Date());
                    }
                    lastTimerCall = now;
                }
            }
        };
        
        vClock = LcdClockBuilder.create()
                                 .prefWidth(760)
                                 .prefHeight(400)
                                 .title("JavaFX")
                                 .styleClass(LcdClock.STYLE_CLASS_YOCTOPUCE)
                                 .foregroundShadowVisible(true)
                                 .crystalOverlayVisible(true)
                                 .timeFont(LcdClock.LcdFont.LCD)
                                 .locale(Locale.ENGLISH)
                                 .dateFormat(LcdClock.DateFormat.DAY_MONTH_YEAR)
                                 .dateSeparator(LcdClock.DateSeparator.DOT)
                                 .alarms(new Alarm[]{
                                     new Alarm(Alarm.Repetition.ONCE, LocalDateTime.now().plusSeconds(30), Alarm.ARMED, "30s after Start", vAlarmEvent)
                                 })
                                 .build();

        vClock.addEventHandler(Alarm.AlarmEvent.ALARM, (Alarm.AlarmEvent alarmEvent) -> {
            vClock.setTitle("Hello Wim :)");
        });

   final StringConverter<LocalDateTime> STRING_CONVERTER = new StringConverter<LocalDateTime>() {
            @Override public String toString(LocalDateTime localDateTime) {
                DateTimeFormatter dtf = DateTimeFormatter.ofPattern("dd.MM.yy\nHH:mm:ss");
                return dtf.format(localDateTime);
            }
            @Override public LocalDateTime fromString(String s) {
                return LocalDateTime.parse(s);
            }
        };        

        NumberAxis yAxis = new NumberAxis();
        yAxis.setLabel("Temperature [°C]");

        DateAxis310 xAxis = new DateAxis310();
        xAxis.setTickLabelFormatter(STRING_CONVERTER);
        
        XYChart.Series vSerieBath = new XYChart.Series<LocalDateTime, Number>();
        vSerieBath.setName("Bathroom");
        XYChart.Series vSerieLiving = new XYChart.Series<LocalDateTime, Number>();
        vSerieLiving.setName("Kitchen");
        
        lineChart = new LineChart<>(xAxis, yAxis);
        lineChart.setTitle("Temperature chart");
        lineChart.getData().addAll(vSerieBath,vSerieLiving);
        lineChart.setAnimated(false);
        xAxis.setTickLabelFormatter(STRING_CONVERTER);
                
        addDataBath(vSerieBath); //turn off RPi 2
        addDataLiving(vSerieLiving);
        applyDataPointMouseEvents(vSerieBath); //turn off RPi 2
        applyDataPointMouseEvents(vSerieLiving);
    }

    @Override public void start(Stage stage) throws Exception {
      stage.setTitle("Pi Home v2");
      Group root = new Group();
      Scene scene = new Scene(root, 600, 450, Color.LIGHTGRAY);
     
      TabPane tabPane = new TabPane();
      BorderPane mainPane = new BorderPane();
     
      //Add living tab
      Tab vTabLiving = new Tab();
      vTabLiving.setClosable(false);
       VBox vLivingBox = new VBox();
       vTabLiving.setText("Kitchen");
       HBox vTitlePane = new HBox();
       Text vTitle = new Text();
       vTitle.setText("Kitchen overview");
       vTitle.setStyle("-fx-font-size:45;");
       vTitlePane.setAlignment(Pos.CENTER);
       vTitlePane.getChildren().add(vTitle); 
      
      HBox vGaugePane = new HBox();
       vGaugePane.setAlignment(Pos.CENTER);
       vGaugePane.setPadding(new Insets(10, 10, 10, 10));
       vGaugePane.setSpacing(10);
       vGaugePane.getChildren().addAll(vThermoMetreKitchten, vHumMetreKitchen);
      
      GridPane vControlPane = new GridPane();
        vControlPane.setAlignment(Pos.TOP_LEFT);
        vControlPane.setPadding(new Insets(40, 0, 0, 50));
        vControlPane.setHgap(10); 
        vControlPane.setVgap(10);
        Text txtAirco = new Text("Airco");
        txtAirco.setStyle("-fx-font-size:20;");
        vControlPane.setHalignment(txtAirco, HPos.RIGHT);

        Text txtHeater = new Text("Heater");
        txtHeater.setStyle("-fx-font-size:20;");
        vControlPane.setHalignment(txtHeater, HPos.RIGHT);
        vControlPane.add(txtHeater, 0, 0);
        vControlPane.add(vHeaterButton, 1, 0);
        vControlPane.add(txtAirco, 0, 1);
        vControlPane.add(vAircoButton, 1, 1);
      
      vLivingBox.getChildren().addAll(vTitlePane,vGaugePane, vControlPane);
      vTabLiving.setContent(vLivingBox);
      tabPane.getTabs().add(vTabLiving);
      
      //Bathroom tab
       Tab vTabLivingBath = new Tab();
       vTabLivingBath.setClosable(false);
        VBox vLivingBoxBath = new VBox();
        vTabLivingBath.setText("Bathroom");
        HBox vTitlePaneBath = new HBox();
        Text vTitleBath = new Text();
        vTitleBath.setText("Bathroom overview");
        vTitleBath.setStyle("-fx-font-size:45;");
        vTitlePaneBath.setAlignment(Pos.CENTER);
        vTitlePaneBath.getChildren().add(vTitleBath); 
      
      HBox vGaugePaneBath = new HBox();
       vGaugePaneBath.setAlignment(Pos.CENTER);
       vGaugePaneBath.setPadding(new Insets(10, 10, 10, 10));
       vGaugePaneBath.setSpacing(10);
       vGaugePaneBath.getChildren().addAll(vThermoMetreBath, vHumMetreBath);
      
      vLivingBoxBath.getChildren().addAll(vTitlePaneBath,vGaugePaneBath);
      vTabLivingBath.setContent(vLivingBoxBath);
      tabPane.getTabs().add(vTabLivingBath);
      
      //Clock tab
      Tab tabC = new Tab();
      tabC.setClosable(false);
        tabC.setText("Clock");
        StackPane vClockPane = new StackPane();
        vClockPane.setPadding(new Insets(10, 10, 10, 10));
        vClockPane.getChildren().setAll(vClock);
        tabC.setContent(vClockPane);
      
       //Chart tab
      Tab vChartTab = new Tab();
        vChartTab.setClosable(false);
        vChartTab.setText("Charts");
        StackPane vChartPane = new StackPane();
        vChartPane.setPadding(new Insets(0, 20, 0, 0));
        vChartPane.getChildren().addAll(lineChart);
        vChartTab.setContent(vChartPane);
        
       //Cam tab
       Tab vCamTab = new Tab();
        vCamTab.setClosable(false);
        vCamTab.setText("Camera");
        StackPane vCamPane = new StackPane();
        vCamView = new ImageView();
        vCamView.setOnMouseReleased((MouseEvent event) -> {
            refreshCam(vCamView);
      });
        vCamView.setFitWidth(500);
        vCamView.setFitHeight(500);
        vCamView.setSmooth(true);
        vCamView.setPreserveRatio(true);
        refreshCam(vCamView);
        vCamPane.setPadding(new Insets(0, 20, 0, 0));
        vCamPane.getChildren().addAll(vCamView);
        vCamTab.setContent(vCamPane);
        
      tabPane.getTabs().add(vChartTab);
      tabPane.getTabs().add(tabC);
      tabPane.getTabs().add(vCamTab);
      mainPane.setCenter(tabPane);
     
      mainPane.prefHeightProperty().bind(scene.heightProperty());
      mainPane.prefWidthProperty().bind(scene.widthProperty());
     
      root.getChildren().add(mainPane);
      stage.setScene(scene);
      stage.show();
      timer.start();
    }
    
    private SimpleGauge createTempGauge(int pSize)
    {
        return SimpleGaugeBuilder.create()
                                        .prefSize(pSize, pSize)
                                        .maxValue(35)
                                        .minValue(5)
                                        .sections(
                                                  new Section(5, 15, "1"),
                                                  new Section(15, 25, "4"),
                                                  new Section(25, 35, "5"))
                                        .unit("C°")         
                                        .title("Temperature")
                                        .value(20)                                        
                                        .styleClass(SimpleGauge.STYLE_CLASS_BLUE_TO_RED_5)
                                        .build();
    }
    
    private SimpleGauge createHumGauge(int pSize)
    {
        return SimpleGaugeBuilder.create()
                                      .prefSize(pSize, pSize)
                                      .sections(new Section(0, 16.66666, "0"),
                                                new Section(16.66666, 33.33333, "1"),
                                                new Section(33.33333, 50.0, "2"),
                                                new Section(50.0, 66.66666, "3"),
                                                new Section(66.66666, 83.33333, "4"),
                                                new Section(83.33333, 100.0, "5"))
                                      .unit("%")
                                      .title("Humidity")
                                      .measuredRangeVisible(true)
                                      .rangeFill(Color.rgb(0, 0, 200, 0.5))                                      
                                      .styleClass(SimpleGauge.STYLE_CLASS_GREEN_TO_DARKGREEN_6)
                                      .build();
    }
    
    private IconSwitch createPowerIconSwitch()
    {
        IconSwitch vIconSwitch = new IconSwitch();
        vIconSwitch.setSymbolColor(Color.WHITE);
        vIconSwitch.setSymbolType(SymbolType.POWER);
        vIconSwitch.setSwitchColor(Paint.valueOf("black"));
        vIconSwitch.setThumbColor(Paint.valueOf("black"));
        return vIconSwitch;
    }
    
    private IconSwitch createAircoSwitch(int pRoomId)
    {
        IconSwitch vIconSwitch = createPowerIconSwitch();
        vIconSwitch.setOnDeselect((SelectionEvent se) -> {
            client.controlAirco(pRoomId, 0);
        });
        vIconSwitch.setOnSelect((SelectionEvent se) -> {
            client.controlAirco(pRoomId, 1);
        });
        return vIconSwitch;
    }
    
    private IconSwitch createHeatControlSwitch(int pRoomId)
    {
        IconSwitch vIconSwitch = createPowerIconSwitch();
        vIconSwitch.setOnDeselect((SelectionEvent se) -> {
            client.controlHeater(pRoomId, 0);
        });
        vIconSwitch.setOnSelect((SelectionEvent se) -> {
            client.controlHeater(pRoomId, 1);
        });
        return vIconSwitch;
    }

    @Override public void stop() {
    }

    public static void main(final String[] args) {
        Application.launch(args);
    }

    // ******************** Misc **********************************************
    private static void calcNoOfNodes(Node node) {
        if (node instanceof Parent) {
            if (((Parent) node).getChildrenUnmodifiable().size() != 0) {
                ObservableList<Node> tempChildren = ((Parent) node).getChildrenUnmodifiable();
                noOfNodes += tempChildren.size();
                for (Node n : tempChildren) {
                    calcNoOfNodes(n);
                    //System.out.println(n.getStyleClass().toString());
                }
            }
        }
    }
    
    public class AlarmCommand implements Alarm.Command {
        @Override public void execute() {
            System.out.println("ALARM EVENT");
        }
    }
    
    private void addDataLiving(final XYChart.Series SERIES) {
        List<Temperature> vListRoom = client.getRoomTemperatures(1);
        vListRoom = convertTemperatureListToTempValueList(vListRoom, 20);
        List<XYChart.Data<LocalDateTime, Number>> data = new ArrayList<>();
        for(Temperature vTemp: vListRoom)
        {
            Calendar calendar = GregorianCalendar.getInstance(); 
            calendar.setTime(vTemp.getTime());
            int vHour = calendar.get(Calendar.HOUR_OF_DAY);
            int vMonth = calendar.get(Calendar.MONTH);
            int vDay = calendar.get(Calendar.DAY_OF_MONTH);
            int vYear = calendar.get(Calendar.YEAR);
            int vMinute = calendar.get(Calendar.MINUTE);
            //System.out.println("Year:"+vYear +" Month: "+vMonth + " Day: "+vDay+ " Hour:"+vHour+" Min: "+vMinute);
            data.add(new XYChart.Data<>(LocalDateTime.of(vYear, vMonth+1, vDay, vHour, vMinute), vTemp.getTempValue()));
        }
        SERIES.getData().setAll(data);
    }
    
    private void addDataBath(final XYChart.Series SERIES) {
        List<Temperature> vListRoom = client.getRoomTemperatures(2);
        vListRoom = convertTemperatureListToTempValueList(vListRoom, 10);
        List<XYChart.Data<LocalDateTime, Number>> data = new ArrayList<>();
        for(Temperature vTemp: vListRoom)
        {
            Calendar calendar = GregorianCalendar.getInstance(); 
            calendar.setTime(vTemp.getTime());
            int vHour = calendar.get(Calendar.HOUR_OF_DAY);
            int vMonth = calendar.get(Calendar.MONTH);
            int vDay = calendar.get(Calendar.DAY_OF_MONTH);
            int vYear = calendar.get(Calendar.YEAR);
            int vMinute = calendar.get(Calendar.MINUTE);

            //System.out.println("Year:"+vYear +" Month: "+vMonth + " Day: "+vDay+ " Hour:"+vHour+" Min: "+vMinute);
            data.add(new XYChart.Data<>(LocalDateTime.of(vYear, vMonth+1, vDay, vHour, vMinute), vTemp.getTempValue()));
        }

        SERIES.getData().setAll(data);
    }
    
    public static List<Temperature> convertTemperatureListToTempValueList(List<Temperature> pTemperatureList, int pLimit)
    {
        List<Temperature> vTemperatureList = pTemperatureList;
        int pForLimit = pTemperatureList.size() - pLimit - 1;
        List<Temperature> tempList = null;
        if(pTemperatureList.size() > pLimit)
        {
            vTemperatureList = new ArrayList();
            for(int i = pTemperatureList.size()-1 ; i > pForLimit; i--)
            {
                vTemperatureList.add(pTemperatureList.get(i));
            }
            tempList = vTemperatureList;
            Collections.reverse(tempList);
        }        
        return tempList;
    }

    private void applyDataPointMouseEvents(final XYChart.Series SERIES) {        
        Platform.runLater(new Runnable() {
            @Override public void run() {                                                
                Label label = new Label("");
                label.getStyleClass().add("value-label");
                StackPane popupPane = new StackPane(label);
                popupPane.setBackground(new Background(new BackgroundFill(Color.BEIGE, CornerRadii.EMPTY, Insets.EMPTY)));
                popup = new Popup();                
                popup.getContent().add(popupPane);                
                
                for (Object data : SERIES.getData()) {                                    
                    XYChart.Data<LocalDateTime, Number> dataPoint = (XYChart.Data<LocalDateTime, Number>) data;
                    final Node node = dataPoint.getNode();
                                                                            
                    node.setOnMouseEntered(mouseEvent -> {                        
                        label.setText(DTF.format(dataPoint.getXValue()) + "\n" + dataPoint.getYValue() + " °C");
                        popup.setX(mouseEvent.getScreenX() + 20);
                        popup.setY(mouseEvent.getScreenY() - 20);
                        popup.show(SERIES.getNode().getScene().getWindow());
                    });
            
                    node.setOnMouseExited(mouseEvent -> {
                        popup.hide();
                    });
                }
            }
        });
    }
    
    private void refreshCam(ImageView pImg)
    {
        String vUrl = client.controlCamera();
        if(vUrl != null)
        {   
            String vRealUrl = vUrl.substring(vUrl.indexOf("src=") + 5);
            vUrl = vRealUrl.substring(0,vRealUrl.indexOf(".jpg") + 4);
            System.out.println("Img URL: " + vUrl);
            vUrl = vUrl.replace("1.101", "0.234");
            Image img = new Image(vUrl);
            pImg.setImage(img);
        }    
    }
}
